/****************************************************************************
  From the Qt examples
****************************************************************************/

#include <QtGui>
#include <QtNetwork>
#include <QDebug>
#include <QLinkedList>
#include <limits>
#include <QIntValidator>
#include "simuhost.h"
#include "client.h"
#include "iterator"
#include "ui_client.h"

Client::Client(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Client)
{
    ui->setupUi(this);

    // find out which IP to connect to
    QString ipAddress;
    QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
    // use the first non-localhost IPv4 address
    for (int i = 0; i < ipAddressesList.size(); ++i) {
        if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
            ipAddressesList.at(i).toIPv4Address()) {
            ipAddress = ipAddressesList.at(i).toString();
            break;
        }
    }
    // if we did not find one, use IPv4 localhost
    if (ipAddress.isEmpty())
        ipAddress = QHostAddress(QHostAddress::LocalHost).toString();


    ui->portLineEdit->setValidator(new QIntValidator(1, 65535, this));
    ui->minLineEdit->setValidator(new QIntValidator(0, INT_MAX, this));
    ui->maxLineEdit->setValidator(new QIntValidator(ui->minLineEdit->text().toInt(), INT_MAX, this));
    ui->numOfClients->setValidator(new QIntValidator(1, 100, this));
    ui->slideValue->setText("1000");
    ui->statusLabel = new QLabel(tr("This program requires that you run the "
                                "Server as well."));

    numUsers = 3;
    hosts = new QLinkedList<SimuHost*>();
    updateHosts();

    //secureSocket = new QSslSocket(this);
    tcpSocket = new QTcpSocket(this);

    ui->hostLineEdit->setText(ipAddress);
    ui->portLineEdit->setText("");
    ui->minLineEdit->setText("1000000");
    ui->maxLineEdit->setText("5000000");
    ui->numOfClients->setText(QString::number(numUsers));

    timer = new QTimer(this);
    connectedToServer = false;

    connect(ui->hostLineEdit, SIGNAL(textChanged(QString)),
            this, SLOT(enableConnectButton()));
    connect(ui->portLineEdit, SIGNAL(textChanged(QString)),
            this, SLOT(enableConnectButton()));
    connect(ui->connectButton, SIGNAL(clicked()),
            this, SLOT(connectToServer()));
    connect(ui->submitButton, SIGNAL(clicked()),
            this, SLOT(setValues()));
    connect(ui->startButton, SIGNAL(clicked()),
            this, SLOT(startSending()));
    connect(ui->stopButton, SIGNAL(clicked()),
            this, SLOT(stopSending()));
    connect(ui->quitButton, SIGNAL(clicked()), this, SLOT(close()));
    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readMessage()));
    //connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(connectToServer()));
    connect(tcpSocket,SIGNAL(disconnected()),this,SLOT(disconnect()));
    connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(displayError(QAbstractSocket::SocketError)));
    connect(timer, SIGNAL(timeout()), this, SLOT(sendCMsg()));
    connect(tcpSocket,SIGNAL(connected()),this,SLOT(connectedToTheServer()));

    setWindowTitle(tr("Client"));
    ui->portLineEdit->setFocus();
}

Client::~Client() {
    delete ui;
}

void Client::startSending()
{
    if (tcpSocket->isOpen()) {
        timer->start(interval);
    }
}

void Client::stopSending()
{
    timer->stop();
}

void Client::setValues()
{
    // retrieve and set values from UI
    interval = ui->horizontalSlider->value();
    numUsers = ui->numOfClients->text().toInt();

    updateHosts();

    bool success;
    min = ui->minLineEdit->text().toULongLong(&success, 10);

    if (!success) {
        min = 0;
        ui->minLineEdit->setText("0");
    }

    max= ui->maxLineEdit->text().toULongLong(&success, 10);

    if (!success) {
        max = 0;
        ui->maxLineEdit->setText("0");
    }

    // restart the timer with new values when new values are set
    if (timer->isActive()) {
        timer->stop();
        timer->start(interval);
    }
}

void Client::connectToServer()
{
    ui->connectButton->setEnabled(false);

    blockSize = 0;
    tcpSocket->abort();
    qDebug() << "Connecting to " << ui->hostLineEdit->text() << " on port " <<
        ui->portLineEdit->text().toInt();
    tcpSocket->connectToHost(ui->hostLineEdit->text(),
                             ui->portLineEdit->text().toInt());
    ui->statusLabel->setText("Connected to the Server");

}

void Client::readMessage()
{
    qDebug() << "Client " << tcpSocket->socketDescriptor() << " reading.";
    qDebug() << "Original blockSize: " << blockSize;
    QDataStream in(tcpSocket);
    in.setVersion(QDataStream::Qt_4_0);

    if (blockSize == 0) {
        if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
            return;
        in >> blockSize;
    }

    qDebug() << "BlockSize: " << blockSize;

    if (tcpSocket->bytesAvailable() < blockSize)
        return;

    qDebug() << "Recieved the full msg";

    QString message;
    in >> message;

    currentMessage = message;
    //statusLabel->setText(currentMessage);
    //clientTextEdit->append(currentMessage);
    ui->connectButton->setEnabled(true);
    //tcpSocket->disconnectFromHost();
    blockSize = 0;

    qDebug() << "Received: " << message;

    if (!connectedToServer) {
        qDebug() << "not connected to server";
        QStringList address = message.split("::", QString::KeepEmptyParts);
        if (!address.isEmpty()) {
            serverAddress = address.takeFirst();
            serverPort = address.takeFirst().toInt();
            qDebug() << "Disconnecting socket";
            tcpSocket->disconnectFromHost();
        }
    }
}

void Client::displayError(QAbstractSocket::SocketError socketError)
{
    switch (socketError) {
    case QAbstractSocket::RemoteHostClosedError:
        break;
    case QAbstractSocket::HostNotFoundError:
        QMessageBox::information(this, tr("Fortune Client"),
                                 tr("The host was not found. Please check the "
                                    "host name and port settings."));
        break;
    case QAbstractSocket::ConnectionRefusedError:
        QMessageBox::information(this, tr("Fortune Client"),
                                 tr("The connection was refused by the peer. "
                                    "Make sure the fortune server is running, "
                                    "and check that the host name and port "
                                    "settings are correct."));
        break;
    default:
        QMessageBox::information(this, tr("Fortune Client"),
                                 tr("The following error occurred: %1.")
                                 .arg(tcpSocket->errorString()));
    }

    ui->connectButton->setEnabled(true);
}

void Client::enableConnectButton()
{
    ui->connectButton->setEnabled(!ui->hostLineEdit->text().isEmpty()
                                 && !ui->portLineEdit->text().isEmpty());
}

void Client::sendCMsg()
{
    qDebug() << "in sendCMsg";
    /*===================================================================*/
    QString packet = "";

    int i = 0;
    QLinkedList<SimuHost*>::const_iterator iter;
    for (iter = hosts->constBegin(); iter != hosts->constEnd(); iter++) {
        packet.append(QString::number(i++)).append(":");
        packet.append(QString::number((*iter)->generateLoad(min, max))).append(";");
    }
    /*===================================================================*/

    qDebug() << "Socket: "  << tcpSocket->socketDescriptor();

    QString text = "";//clientLineEdit->text();
    text.prepend("Client> ");
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << (quint16)0;
    out << packet;//text;
    out.device()->seek(0);
    out << (quint16)(block.size() - sizeof(quint16));

    qDebug() << "Client writing: " << text << endl;
    qDebug() << "Block: " << block;
    int test;
    test = tcpSocket->write(block);
    if(test == -1)
        qDebug() << "Cant write";
    else
        qDebug() << "Client sent msg..." << endl;
    if(tcpSocket->waitForBytesWritten() == true)
        qDebug() << "Written..." << endl;
    else
        qDebug() << "Failed..." << endl;
}

void Client::sendClientInfo(QString info)
{
    qDebug() << "Socket: "  << tcpSocket->socketDescriptor();

    //ADD ON THE CLIENT INFORMATION
   // info.prepend("Client:: ");
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << (quint16)0;
    out << info;
    out.device()->seek(0);
    out << (quint16)(block.size() - sizeof(quint16));

    qDebug() << "Client sending info: " << info << endl;
    qDebug() << "Block: " << block;
    int test;
    test = tcpSocket->write(block);
    if(test == -1)
        qDebug() << "Cant write";
    else
        qDebug() << "Client sent msg..." << endl;
    if(tcpSocket->waitForBytesWritten() == true)
        qDebug() << "Written..." << endl;
    else
        qDebug() << "Failed..." << endl;
}

void Client::connectedToTheServer()
{
    qDebug() << "Connected to the SERVER!!!";
    sendClientInfo("client");
}

void Client::disconnect()
{
    qDebug() << "disconnected";
    blockSize = 0;
    tcpSocket->abort();

    if (!connectedToServer) {
        tcpSocket->connectToHost(serverAddress,serverPort);
        connectedToServer = true;
    }
    else {
        connectedToServer = false;
        qDebug() << "Disconnected!!!";
        timer->stop();
    }
    //connectToServer();
}

void Client::on_horizontalSlider_valueChanged(int value)
{
    ui->slideValue->setText(QString::number(value));
}

void Client::updateHosts()
{
    if (hosts->size() < numUsers) {
        for(int j = hosts->size(); j < numUsers; j++) {
            hosts->append(new SimuHost());
        }
    }
    else if (hosts->size() > numUsers){
        int i = hosts->size();

        QLinkedList<SimuHost*>::iterator iter;
        for (iter = hosts->end(); i >= hosts->size() && iter != hosts->begin(); iter--) {
            hosts->erase(iter);
        }
    }
}
